﻿using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Xml;
using System.Linq;
namespace Codemonk.Soya
{
    /// <summary>
    /// 文档描述
    /// </summary>
    internal class DocumentDescription
    {
        private string filename;
        public string FileName
        {
            get
            {
                return filename;
            }
            set
            {
                filename = value;
                var document = new XmlDocument();
                document.Load(filename);
                #region 构造Action
                Action<XmlNode, string, Action<XmlNode>> action = (doc, xpath, nodeAction) =>
                {
                    new Action(() =>
                    {
                        var selectedNodes = doc.SelectNodes(xpath);
                        if (selectedNodes == null) return;
                        foreach (XmlNode selectedNode in selectedNodes)
                        {
                            try
                            {
                                nodeAction(selectedNode);
                            }
                            catch { }
                        }
                    }).SafeInvoke();
                };
                Action<string, Action<XmlNode>> reader = (xpath, nodeAction) => action(document, xpath, nodeAction);
                #endregion
                reader("/document/sheetlimit", node => new Action(() => SheetLimit = node.As<Regex>("@regex", ConverterExtensions.AsRegex, null)).SafeInvoke());
                reader("/document/paging", node => new Action(() => Paging = new PagingDescription
                {
                    Regex = node.As<Regex>("@regex", ConverterExtensions.AsRegex, null),
                    ColumnIndex = node.As<int>("@columnindex", ConverterExtensions.AsInt32, -1),
                }).SafeInvoke());
                List<Scheme> schemes = new List<Scheme>();
                reader("/document/schemes/scheme", node =>
                {
                    Scheme scheme = new Scheme();
                    List<ReadRule> readRules = new List<ReadRule>();
                    new Action(() =>
                    {
                        var targetname = node.As<string>("targetname", ConverterExtensions.AsString, null, false);
                        if (string.IsNullOrWhiteSpace(targetname)) return;
                        scheme.TargetName = targetname;
                        action(node, "rules/rule", rule =>
                        {
                            ReadRule readRule = new ReadRule();
                            var columnindex = rule.As<int>("location/@columnindex", ConverterExtensions.AsInt32, int.MinValue); if (columnindex == int.MinValue) return;
                            var rowindex = rule.As<int>("location/@rowindex", ConverterExtensions.AsInt32, int.MinValue);
                            var startrow = rule.As<int>("location/@startrow", ConverterExtensions.AsInt32, int.MinValue);
                            if (startrow == int.MinValue && rowindex == int.MinValue) return;
                            readRule.Location = new Location { ColumnIndex = columnindex, RowIndex = rowindex, StartRow = startrow };
                            Regex regex = rule.As<Regex>("evaluation/simplevalue/regex", ConverterExtensions.AsRegex, null, false);
                            Evaluation evaluation = null;
                            #region 求值逻辑

                            Func<XmlNode, string, ReduceTarget[]> readTargets = (xmlnode, xp) =>
                            {
                                List<ReduceTarget> targets = new List<ReduceTarget>();
                                action(xmlnode, xp, reduce =>
                                {
                                    var groupindex = reduce.As<int>("@groupindex", ConverterExtensions.AsInt32, -1); if (groupindex == -1) return;
                                    var property = reduce.As<string>("@property", ConverterExtensions.AsString, null); if (string.IsNullOrWhiteSpace(property)) return;
                                    var type = reduce.As<Type>("@type", ConverterExtensions.AsType, null); if (type == null) return;
                                    var cannull = reduce.As<bool>("@cannull", ConverterExtensions.AsBoolean, false);
                                    var dvalue = reduce.As<string>("@defaultvalue", ConverterExtensions.AsString, null);
                                    object defaultValue = null;
                                    if (dvalue != null)
                                    {
                                        defaultValue = Converter.As(type, dvalue, null);
                                    }
                                    targets.Add(new ReduceTarget
                                    {
                                        DefaultValue = defaultValue,
                                        Type = type,
                                        Property = property,
                                        GroupIndex = groupindex,
                                        CanNull = cannull
                                    });
                                });
                                return targets.ToArray();
                            };
                            #endregion
                            if (regex != null)
                            {
                                ReduceTarget[] target_array = readTargets(rule, "evaluation/simplevalue/mapreduce/target");
                                if (target_array != null)
                                    evaluation = new SimpleEvaluation { Regex = regex, Reduces = target_array };
                            }
                            else
                            {
                                regex = rule.As<Regex>("evaluation/complexvalue/reduce/regex", ConverterExtensions.AsRegex, null, false);
                                if (regex == null) return;
                                var c_targetname = rule.As<string>("evaluation/complexvalue/targetname", ConverterExtensions.AsString, null, false);
                                if (string.IsNullOrWhiteSpace(c_targetname)) return;
                                Reference reference = null;
                                new Action(() =>
                                {
                                    var r_reg = rule.As<Regex>("evaluation/complexvalue/reference/@regex", ConverterExtensions.AsRegex, null, false);
                                    var r_cindex = rule.As<int>("evaluation/complexvalue/reference/@columnindex", ConverterExtensions.AsInt32, int.MinValue);
                                    if (r_reg != null && r_cindex != int.MinValue) reference = new Reference { Regex = r_reg, ColumnIndex = r_cindex };
                                }).SafeInvoke();
                                ReduceTarget[] target_array = readTargets(rule, "evaluation/complexvalue/reduce/mapreduce/target");
                                if (target_array != null && reference != null) evaluation = new ComplexEvaluation { TargetName = c_targetname, Reduces = target_array, Regex = regex, Reference = reference };
                            }
                            if (evaluation != null)
                            {
                                readRule.Evaluation = evaluation;
                                readRules.Add(readRule);
                            }
                        });
                    }).SafeInvoke();
                    if (readRules.Count > 0)
                    {
                        scheme.ReadRules = readRules.ToArray();
                        schemes.Add(scheme);
                    }
                });
                Shemes = schemes.ToArray();
            }
        }
        /// <summary>
        /// 转换器
        /// </summary>
        public IStringConverter Converter { get; set; }
        /// <summary>
        /// sheet限制
        /// </summary>
        internal Regex SheetLimit { get; set; }
        /// <summary>
        /// 分页描述
        /// </summary>
        internal PagingDescription Paging { get; set; }
        /// <summary>
        /// 规则
        /// </summary>
        internal Scheme[] Shemes { get; set; }
    }
    /// <summary>
    /// 分页描述
    /// </summary>
    internal class PagingDescription
    {
        /// <summary>
        /// 列索引
        /// </summary>
        internal int ColumnIndex { get; set; }
        /// <summary>
        /// 分页正则
        /// </summary>
        internal Regex Regex { get; set; }
    }
    /// <summary>
    /// 读取规则
    /// </summary>
    internal class Scheme
    {
        internal string TargetName { get; set; }
        internal ReadRule[] ReadRules { get; set; }
    }

    internal class ReadRule
    {
        /// <summary>
        /// 位置
        /// </summary>
        internal Location Location { get; set; }
        /// <summary>
        /// 求值
        /// </summary>
        internal Evaluation Evaluation { get; set; }
    }

    internal class Location
    {
        /// <summary>
        /// 列索引
        /// </summary>
        internal int ColumnIndex { get; set; }
        /// <summary>
        /// 相对行索引
        /// </summary>
        internal int RowIndex { get; set; }
        /// <summary>
        /// 起始行
        /// </summary>
        internal int StartRow { get; set; }
    }

    internal abstract class Evaluation
    {
        /// <summary>
        /// 正则
        /// </summary>
        internal Regex Regex { get; set; }
        /// <summary>
        /// 值分发
        /// </summary>
        internal ReduceTarget[] Reduces { get; set; }
    }

    internal class SimpleEvaluation : Evaluation { }
    internal class ComplexEvaluation : Evaluation
    {
        internal string TargetName { get; set; }
        internal Reference Reference { get; set; }
    }

    internal class Reference
    {
        /// <summary>
        /// 列索引
        /// </summary>
        internal int ColumnIndex { get; set; }
        /// <summary>
        /// 正则
        /// </summary>
        internal Regex Regex { get; set; }
    }

    /// <summary>
    /// 值分发
    /// </summary>
    internal class ReduceTarget
    {
        /// <summary>
        /// 组索引
        /// </summary>
        internal int GroupIndex { get; set; }
        /// <summary>
        /// 值类型
        /// </summary>
        internal Type Type { get; set; }
        /// <summary>
        /// 属性名称
        /// </summary>
        internal string Property { get; set; }
        /// <summary>
        /// 默认值
        /// </summary>
        internal object DefaultValue { get; set; }
        /// <summary>
        /// 默认值
        /// </summary>
        internal bool CanNull { get; set; }
    }
}
